#include "Lily_help.h"
#include <math.h>
#include <string.h>
// #include <malloc.h>

typedef char* str;
const char operations[] = "^*/%+-";
const char operations_priorities[] = { 0, 1, 1, 2, 3, 3 }; //must in ascending

// replace \" \' to toChar
// return replace times
int32_t str_quote_merge(char* s, char toChar)
{
	int32_t n = 0;
	while (*s != '\0')
	{
		if (*s == '\"' || *s == '\'') // enter
		{
			*s = ' ';
			n++;
			s++;
			while (*s != '\0')
			{
				if (*s == ' ')
				{
					*s = toChar;
				}
				else if (*s == '\'' || *s == '\"') //leave
				{
					n++;
					*s = ' ';
					break;
				}
				s++;
			}
			if (n % 2 != 0)
			{
				return -1;
			}
			if (*s == '\0')
			{
				return n;
			}
		}
		s++;
	}
	return 0;
}

void resume_str_by_quote(int32_t n, str* ss, char from, char to)
{
	int32_t i;
	for (i = 0; i < n; i++)
	{
		str_replace(ss[i], from, to);
	}
}

/// <summary>
/// cal a exp, change rx
/// assart: no space in rx
/// </summary>
/// <exception>
/// malloc error
/// number not match
/// field not numeric
/// divided by zero
/// <param name="rx"></param>
/// <returns></returns>
float shell_calculate_expression(char* rx)
{
	//get indexes of operations
	Li_List opes_index = str_find(rx, operations);
	int32_t* indexes = list_content(opes_index, int32_t);
	int32_t n = opes_index->count;
	// get operations
	char* opes = (char*)malloc(n + 1);
	if (opes == NULL || opes_index == NULL)
	{
		delete_list(opes_index);
		__li_error_msg = "malloc error";
		free(opes);

		return __Nanf;
	}

	int32_t i, j = 0;
	for (i = 0; i < n; i++)
	{
		int32_t at = indexes[i];
		if (rx[at] == '-')
		{
			if (at == 0 || rx[at - 1] == '(' || str_contains(operations, rx[at - 1])) // like -3+4; ||rx[at - 1] == '-'
			{
				rx[at] = '&'; //replace '-' to '&'
				continue;
			}
		}
		opes[j++] = rx[at];
	}

	opes[j] = '\0';
	n = j;
	delete_list(opes_index);

	// get numbers/fields, in form of string
	//operations[4] = ' ';
	Li_List list = str_split_by_str(rx, operations);
	if (list->count - n != 1)
	{
		delete_list(list);
		//lily_error_msg = "number not match";
		free(opes);

		li_error("number not match", __Nanf);
		//return Nanf;
	}
	//resume '-' from '&'
	str* labes = list_content(list, str);
	for (i = 0; i < list->count; i++)
	{
		if (labes[i][0] == '&')
			labes[i][0] = '-';
	}

	Li_List values_ = _values_of_vars(list);
	delete_list(list);
	if (values_ == NULL)
	{
		__li_error_msg = "field missing or not numeric";
		free(opes);

		return __Nanf;
	}
	float* values = list_content(values_, float);

	// start calculate on by on
	// e.g.:opes: ++*+/
	for (i = 0; operations[i] != '\0';)
	{
		n = str_index(opes, operations[i]); //wrong way
		if (n < 0)
		{
			i++; // to next operation[* / + -]
			continue;
		}
		//adjust to the first operator in the same priority
		int32_t n_prioriyt = operations_priorities[i];
		while (n > 0)
		{
			int32_t poi = str_index(operations, opes[n - 1]);
			if (operations_priorities[poi] == n_prioriyt)
			{
				n--; // to last ope
			}
			else
			{
				break;
			}
		}
		// operations[i], at index n in opes,
		// get nums at n and n+1, using operations[i] to calculate
		switch (opes[n])
		{
		case '^': //*
			values[n] = powf(values[n], values[n + 1]);
			break;
		case '*': //*
			values[n] = values[n] * values[n + 1];
			break;
		case '/': // /
			if (values[n + 1] == 0.0f)
			{
				__li_error_msg = "divided by zero";
				delete_list(values_);
				free(opes);

				return __Nanf;
			}
			values[n] = values[n] / values[n + 1];
			break;
		case '%': // +
			values[n] = fmodf(values[n], values[n + 1]);
			break;
		case '+': // +
			values[n] = values[n] + values[n + 1];
			break;
		case '-': // -
			values[n] = values[n] - values[n + 1];
			break;
		default:
			break;
		}
		// remove
		list_remove_at(values_, n + 1);
		int32_t j;
		for (j = n; opes[j] != '\0'; j++)
			opes[j] = opes[j + 1];
	}
	float result = values[0];
	delete_list(values_);
	free(opes);

	return result;
}

static char local_var = 'a';
/// <summary>
///
/// </summary>
/// <exceptions>
/// brace not match
/// mem over
/// </exceptions>
/// <param name="exp"></param>
/// <returns></returns>
float cal_more(char* exp)
{
	char tx[TX_len];
	int32_t left, right = -1;
	left = str_index(exp, '(');
	if (left < 0)
	{
		return shell_calculate_expression(exp);
	}

	int32_t j, n = 0;

	for (j = left + 1; exp[j] != '\0'; j++)
	{
		if (exp[j] == '(')
			n++;
		else if (exp[j] == ')')
		{
			if (n == 0)
			{
				right = j;
				break;
			}
			n--;
		}
	}
	if (right < 0)
	{
		li_error("brace not match", __Nanf);
	}
	exp[right] = '\0'; // islate sub string
	float _res = cal_more(exp + left + 1);
	if (isnan(_res))
	{
		return _res;
	}
	strncpy(tx, exp, left);
	j = left;
	tx[j++] = '_';
	tx[j++] = local_var;
	char vn[3] = "_a";
	vn[1] = local_var++;
	int32_t tmp_var_indx = search_var_in_Lily_ui(vn);
	if (tmp_var_indx < 0)
	{
		if (public_a_new_var(vn, 'f', to_voidf(_res)) < 0)
		{
			li_error("mem over", __Nanf);
		}
	}
	else //temp var already exited
	{
		li_vars[tmp_var_indx].val.vf = _res;
		// assign var from result
	}
	for (right++; exp[right] != '\0'; right++)
	{
		tx[j++] = exp[right];
	}
	tx[j] = '\0';
	strcpy(exp, tx);
	return cal_more(exp);
}
/* to calculate complex expression like (1+a)*3+(b+4)*2
usage:	result = _cal_delegate(exp);
		if (isnan(result))
		{
			return -1;
		}

*/
float _cal_delegate(char* exp)
{
	local_var = 'a';
	str_wrap(exp, ' ');
	float result = cal_more(exp);
	// delete unused var in cal
	local_var--;
	while (local_var >= 'e')
	{
		char s[3] = "_e";
		s[1] = local_var--;
		// string ss[1] = {&s};//@issue: note here may wrong
		str ss[1] = { (str)s };
		cmd_delete(1, ss);
	}
	return result;
}
int32_t shell_cal_exp_cmd(int32_t n, char** arg)
{
	char tx[TX_len];
	float result;
	if (n == 2)
	{
		result = _cal_delegate(arg[1]);
		if (isnan(result)) //(Nanf == result)
		{
			return -1;
		}

		//if (result == NAN)return - 1;
		sprintf(tx, "%f\n", result);
		lily_out(tx);
	}
	else if (n == 3)
	{
		result = _cal_delegate(arg[2]);
		if (isnan(result))
			return -3;

		int32_t index = search_var_in_Lily_ui(arg[1]);

		int32_t at;
		if (index < 0)
		{
			at = 0;
			index = public_a_new_var(arg[1], 'f', 0);
		}
		else
		{
			at = _ebrace_value_from_string(arg[1]);
			if (at == -1)
			{
				at = 0;
			}
			else if (at < -1)
			{
				li_error("index error", -__LINE__);
			}
		}
		Var var = li_vars + index;
		int32_t rtc = 0;
		if (var->type == 'f')
		{
			rtc = set_value_of_var(var, at, to_voidf(result));
		}
		else if (var->type == 'd')
		{
			rtc = set_value_of_var(var, at, to_void((int32_t)result));
		}
		else
		{
			li_error("field is not numeric", -__LINE__);
		}
		if (rtc < 0)
		{
			li_error("assign error", rtc);
		}

		lily_out(arg[1]);
		sprintf(tx, "=%f\n", result);
		lily_out(tx);
	}
	return 0;
}
// add raw mode to allow operations for raw memory, which is triggered by "(" or ")"
// use like a[1]=f("str1","str2",3)
// it's not safe, may cause memory leak

// a = 1+1
// a+1
// 1+1
int32_t shell_do_cal(char* rx)
{
	if (!str_contains_by_str(rx, (char*)operations))
		return 0;
	if (str_contains_by_str(rx, (char*)"\"\'")) // like var1 = "new string -f -a"
		return 0;

	int32_t ind_equ = str_index(rx, '=');
	if (ind_equ >= 0)
	{
		//check
		rx[ind_equ] = '\0';

		int32_t bbc = str_index(rx, '[');
		if (bbc >= 0)
		{
			rx[bbc] = '\0';
			if (!str_is_name(rx)) //var[]
				return 0;		  //check failed
			rx[bbc] = '[';
		}
		else
		{
			if (!str_is_name(rx)) //var[]
				return 0;		  //check failed
		}

		rx[ind_equ] = '=';
		ind_equ++;
		while (rx[ind_equ] == ' ')
			ind_equ++;
	}
	Li_List li = str_split(rx, '=');
	if (li->count > 2)
	{
		delete_list(li);
		return 0;
	}
	str* strs = list_content(li, str);
	str args[3];
	args[0] = (char*)"cal";
	args[1] = strs[0];
	if (li->count == 2)
		args[2] = strs[1];
	int32_t code = shell_cal_exp_cmd(li->count + 1, args);
	delete_list(li);
	if (code < 0)
		return code;
	return 1;
}

int32_t shell_do_notFound(char* rx)
{
	lily_out("\ano cmd:\"");
	lily_out(rx);
	lily_out("\"\n");
	return 1;
}

#define vvar (var->val)
//a[n]=b[m]
// numerically assignation
int32_t _assign_var_from_var(Var dst, Var src, int32_t at_dst, int32_t at_src)
{
	float vs;
	void* vp;
	vp = get_value_of_var(src, at_src);

	switch (src->type)
	{
	case 'f':
		assign_f_from_void(vs, vp);
		break;
	case 'd':
	case 'c':
		vs = void_to_int(vp);
		break;
	default:
		li_error("undefined type", -3);
		//		break;
	}

	switch (dst->type)
	{
	case 'f':
		set_value_of_var(dst, at_dst, to_voidf(vs));
		break;
	case 'd':
		set_value_of_var(dst, at_dst, to_void(((int32_t)vs)));
		break;
	case 'c':
		set_value_of_var(dst, at_dst, to_void(((char)vs)));
		break;
	default:
		li_error("undefined type", -4);
		//		break;
	}

	return 0;
}
// val: a string like '123.4' for numeric var
// or any string for a char* var
// or a string for a string var/array
int32_t _assign_var_from_string(Var dst, char* val, int32_t at)
{
	float val_f;
	int32_t val_i;
	int32_t erc = 0;
	switch (dst->type)
	{
	case 'c':
		if (at) // must start from head
		{
			li_error("type error", -__LINE__);
		}
		erc = _assign_string_var(dst, val);
		break;
	case 'd':
	case 'f':
		if (str_is_numeric(val))
		{
			if (dst->type == 'd')
			{
				val_i = atoi(val);
				erc = set_value_of_var(dst, at, to_void(val_i));
			}
			else
			{
				val_f = atof(val);
				erc = set_value_of_var(dst, at, to_voidf(val_f));
			}
		}
		else // s -> n
		{
			li_error("bad assign", -__LINE__);
		}
		break;
	case 's':
		if (dst->isa)
		{
			assert_not_empty(dst->val.ps);
			assert_true(dst->len > at);
			str s = dst->val.ps[at]; //
			if (s == NULL)
			{
				dst->val.ps[at] = new_string_by(val);
			}
			else
			{
				int32_t n = strlen(val) + 1;
				s = realloc(s, n);
				assert_not_empty(s);
				dst->val.ps[at] = s;
				strcpy(s, val);
			}
		}
		else
		{
			str s = dst->val.vs; //
			if (s == NULL)
			{
				dst->val.vs = new_string_by(val);
			}
			else
			{
				int32_t n = strlen(val) + 1;
				s = realloc(s, n);
				assert_not_empty(s);
				dst->val.vs = s;
				strcpy(s, val);
			}
		}
	default:
		break;
	}
	return erc;
}
/// <summary>
/// assign a string var
/// var="hello"
/// </summary>
/// <param name="var"></param>
/// <param name="val"></param>
/// <returns>-1 if error</returns>
int32_t _assign_string_var(Var var, const char* val)
{
	if (var->isb)
		return -2; //protected cannot write
	if (var->type != 's' /*|| !var->isa*/)
		return -3; //type error
	int32_t n = strlen(val);
	void* p;
	if (vvar.pc != NULL)
		p = realloc(vvar.pc, n + 1);
	else
	{
		p = malloc(n + 1); // init
	}

	if (p == NULL)
		return -1;
	vvar.pc = p;
	strcpy((char*)(p), val);
	var->len = n + 1;
	return 0;
}
// return value of var[at] in string type
// note: if var is in type 'c' and is a array,
//   it will return whole string, not a number in string
void _var_to_string(char* tx, Var var, int32_t at)
{
	if (at && !var->isa)
	{
		*tx = '\0'; //index error
		return;
	}
	if (var->type == 's')
	{
		if (var->isa)
		{
			if (at >= var->len)
			{
				*tx = '\0'; //index out
				return;
			}
			strcpy(tx, vvar.ps[at]);
		}
		else
		{
			strcpy(tx, vvar.vs);
		}
		return;
	}
	//if (var->type == 'c' && var->isa)
	//{
	//	if (at >= var->len)
	//	{
	//		*tx = '\0'; //index out
	//		return;
	//	}
	//	//char *p = cast(vvar.pc, char *);
	//	strcpy(tx, vvar.pc + at);
	//	return;
	//}
	void* val = get_value_of_var(var, at);
	float vf;
	switch (var->type)
	{
	case 'f':
		assign_f_from_void(vf, val);
		sprintf(tx, "%f", vf);
		break;
	case 'd':
		//case 'c':
		// _itoa(void_to_int(val), tx, 10);
		sprintf(tx, "%d", void_to_int(val));
		break;
	case 'c':
		// _itoa(void_to_int(val), tx, 10);
		sprintf(tx, "%c", cast(val, char));
		break;
	default:
		break;
	}
}
/// <summary>
/// get value of vars of number in string
/// fixed: -var
/// </summary>
/// <param name="list">a string list, indicates the name of vars or just numbers,like ['var1','-123','-var2']</param>
/// <returns>list of float vlaue,
/// return NULL if errors occured </returns>
Li_List _values_of_vars(Li_List list)
{
	str* item_strs = list_content(list, str);
	int32_t n = list->count;
	Li_List values = new_li_cap(float, n);
	int32_t i;
	for (i = 0; i < n; i++)
	{
		str name = item_strs[i];
		char sign_plus = 1;
		if (name[0] == '-')
		{
			sign_plus = 0;
			name++;
		}
		else if (name[0] == '+')
		{
			sign_plus = 1;
			name++;
		}

		float val = _value_from_string_or_var(name);
		if (isnan(val))
		{
			delete_list(values);
			return NULL;
		}
		if (!sign_plus)
		{
			val = -val;
		}
		li_add(values, val);
	}
	return values;
}

/*get value from a string,support array index, only the value not string
support like:
a
123.4
a[1], field type 'f,d,c'
a[b[n]]

no \w or space in name like: " a[2]", "a[ 1]"
cannot make any calculations between '[' and ']'
return NAN if in error*/
float _value_from_string_or_var(str name)
{
	float val;
	int32_t idx1 = str_index(name, '[');
	int32_t idx2 = str_index_right(name, ']');
	if (idx1 > 0 && idx2 > 0) //a[i]
	{
		name[idx1] = '\0';
		name[idx2] = '\0';
	}
	if (str_is_name(name))
	{
		int32_t index = search_var_in_Lily_ui(name);

		if (index < 0)
		{
			return __Nanf;
		}

		int32_t at = 0;
		float vid = 0;
		if (idx1 > 0 && idx2 > 0) //a[i]
		{
			float vid = _value_from_string_or_var(name + idx1 + 1);
			if (isnan(vid))
				return __Nanf;
			if (vid - cast(vid, int32_t) != 0.0f) //index must be a intergal
				return __Nanf;
			at = vid;
		}
		else if (!(idx1 < 0 && idx2 < 0))
			return __Nanf;

		Var var = li_vars + index;

		void* p = get_value_of_var(var, at);

		switch (var->type)
		{
		case 'f':
			assign_f_from_void(vid, p);
			break;
		case 'd':
		case 'c':
			vid = void_to_int(p);
			break;
		default:
			break;
		}
		return vid;
	}
	else if (str_is_numeric(name))
	{
		val = atof(name);
	}
	else
	{
		return __Nanf;
	}
	return val;
}

//return the index in "[]",
//like 'a[1]', a[b[n]]
// return -1 for error
// return -2 if index is not a intergal
// !note:if no [], -1 will be returned
// if a bad expression in [], -2 returned
// if expression result a decimal, return -3
int32_t _ebrace_value_from_string(str name)
{
	int32_t at = -1;
	int32_t idx1 = str_index(name, '[');
	int32_t idx2 = str_index_right(name, ']');

	if (idx1 > 0 && idx2 > 0) //a[i]
	{
		name[idx1] = '\0';
		name[idx2] = '\0';
		float vid = _value_from_string_or_var(name + idx1 + 1);
		if (isnan(vid))
			return -2;
		if (vid - cast(vid, int32_t) != 0.0f) //index must be a intergal
			return -3;
		at = vid;
	}
	return at;
}
int32_t _ebrace_value_from_string_out(str name, char** out)
{
	int32_t at = -1;
	int32_t idx1 = str_index(name, '[');
	int32_t idx2 = str_index_right(name, ']');

	if (idx1 > 0 && idx2 > 0) //a[i]
	{
		name[idx1] = '\0';
		name[idx2] = '\0';
		*out = name + idx2 + 1;
		float vid = _value_from_string_or_var(name + idx1 + 1);
		if (isnan(vid))
			return -2;
		if (vid - cast(vid, int32_t) != 0.0f) //index must be a intergal
			return -3;
		at = vid;
	}
	return at;
}

// assign, create, and show vars
// >>a=1, a[2]=b, a[b[c]]=d, a=hello, a[4]
// type conversion
// >>a=123 $C, a $f,a $F, b $C
int32_t shell_do_var(char* rx)
{
	char tx[TX_len];
	int32_t equ_idx = str_index(rx, '=');
	int32_t given_type = str_index(rx, '$');
	char* rx1 = rx, * rx2 = NULL; // rx='a=123', rx1='a', rx2='123'

	if (given_type > 0)
	{
		rx[given_type] = '\0';
		given_type = rx[given_type + 1]; // Type
	}

	if (equ_idx > 0)
	{
		rx[equ_idx] = '\0';
		rx2 = rx + equ_idx + 1;
		rx2 += str_left_wrap(rx2, ' ');
		str_wrap_by_str(rx2, "\"\'");
	}

	int32_t bbc = str_index(rx1, '[');
	if (bbc >= 0)
	{
		rx1[bbc] = '\0';
		if (!str_is_name(rx1)) //var[]
			return 0;		   //check failed
		rx1[bbc] = '[';
	}
	else
	{
		if (!str_is_name(rx1)) //var[]
			return 0;		   //check failed
	}
	int32_t var1_idx = search_var_in_Lily_ui(rx1);

	if (var1_idx < 0 && rx2 == NULL) // hit miss, >>adf
	{
		// let the next process to deal
		return 0; //do not dealing
	}

	Var var1 = NULL;

	if (var1_idx >= 0)
		var1 = li_vars + var1_idx;

	int32_t code = 0;
	float rx2_value = __Nanf;
	if (rx2 != NULL)
	{
		/*if (toLower(given_type) == 'd')
		{
			str_left_wrap(rx2, ' ');
		}*/
		rx2_value = _value_from_string_or_var(rx2); //numeric value
	}

	int32_t brace_val = _ebrace_value_from_string(rx1);
	if (brace_val < -1) // bad expression
	{
		li_error("index must be a intergal", -__LINE__);
	}

	// creat new Field
	if (var1 == NULL && rx2 != NULL) // e.g.: newField = a
	{
		//determine the type of var1
		//rx2_value = _value_from_string_or_var(rx2);
		char type;
		if (given_type > 0)
			type = given_type;
		else if (isnan(rx2_value))
			type = 's'; //string type
		else
			type = 'f';

		//if (type != 's' && isnan(rx2_value))// need a number type, but not given
		//	return -12; // error or not match

		if (type == 's')
		{
			code = public_a_new_var(rx1, type, rx2);
			//code = public_new_vars_array(rx1, 'C', rx2, strlen(rx2) + 1);
		}
		else //determine to create a array or var
		{
			//aessert rx2_value is not NAN
			if (isUpper(type))
			{
				code = public_new_vars_array_by_string(rx1, type, rx2, brace_val); //assert brace_val>0
				brace_val = 0;
			}
			else //single
			{
				if (type == 'f')
					code = public_a_new_var(rx1, type, to_voidf(rx2_value));
				else if (type == 'd' || type == 'c')
					code = public_a_new_var(rx1, type, to_void((int32_t)rx2_value));
				else
				{
					li_error("type error", -__LINE__);
				}
			}
		}

		if (code < 0)
		{
			return code;
		}
		else
		{
			var1 = li_vars + code;
		}
	}
	else if (var1 != NULL && rx2 != NULL) //reassign a field
	{
		// a
		if (isUpper(given_type) && !var1->isa) //assign as a array
		{
			//but var is not an array
			li_error("bad type cover", -__LINE__);
		}

		if (given_type > 0)
		{
			if (var1->type == 's' && given_type != 's')
			{
				li_error("cannot change str type", -__LINE__);
			}
			// directly change fields
			var1->isa = isUpper(given_type);
			var1->type = toLower(given_type);
		}
		if (var1->type == 's')
		{
			if (var1->isa) //strs[2]=abs
			{
				int32_t n = strlen(rx2) + 1;
				if (brace_val < 0 || brace_val > var1->len)
				{
					li_error("bad index", -5);
				}
				char* p = realloc(var1->val.ps[brace_val], n + 1);
				if (p == NULL)
				{
					li_error("mem error", -__LINE__);
				}
				else
				{
					var1->val.ps[brace_val] = p;
				}
				strcpy(var1->val.ps[brace_val], rx2);
			}
			else
				code = _assign_string_var(var1, rx2);
		}
		/*if (var1->type == 'c' && var1->isa)
		{
			code = _assign_string_var(var1, rx2);
		}*/
		else if (isnan(rx2_value))
		{
			// if (var1->type == 'c' && var1->isa)
			// 	_assign_string_var(var1, rx2);
			// else
			__li_error_msg = "value not numeric";
			code = -33; //assign error
		}
		else // assign number
		{
			int32_t bv_ = brace_val;
			if (brace_val < 0)
			{
				brace_val = 0;
			}

			if (var1->type == 'f')
				code = set_value_of_var(var1, brace_val, to_voidf(rx2_value));
			else if (var1->type == 'd' || var1->type == 'c')
				code = set_value_of_var(var1, brace_val, to_void((int32_t)rx2_value));
			else
			{
				li_error("type error", -__LINE__);
			}
			brace_val = bv_;
		}
		if (code < 0)
		{
			// #define SCAT(A,B,C) (A##B##C)
			li_error("assign failed", -__LINE__);
			// return code;
		}
	}

	//show
	lily_out(var1->name);
	if (brace_val >= 0)
	{
		sprintf(tx, "[%d]", cast(brace_val, int32_t));
		lily_out(tx);
	}
	lily_out("=");
	if (brace_val < 0)
	{
		if (var1->type == 's')
		{
			lily_out(var1->val.vs);
		}
		else {
			_var_to_string(tx, var1, 0);
			lily_out(tx);
		}
	}
	else
	{
		if (var1->type == 's' && var1->isa)
		{
			lily_out(var1->val.ps[brace_val]);
		}
		else
		{
			_var_to_string(tx, var1, brace_val);
			lily_out(tx);
		}
	}
	lily_out("\n");
	return 1;
}

#undef vvar
